package top.idcard.agent.biz.rw.channel;

import org.reflections.Reflections;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
import top.idcard.agent.biz.rw.read.AbstractRead;
import top.idcard.agent.biz.rw.read.Read;
import top.idcard.agent.biz.rw.write.AbstractWrite;

import java.io.Closeable;
import java.lang.reflect.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wx
 * @version 1.0
 * @date 2021/2/3 2:42 下午
 */
public class RWContext<I extends Closeable, D> {

    private AbstractRead<I,D> read;
    private List<AbstractWrite<D>> writeList = new ArrayList<>();
    private D data;
    private static Map<Class, RWContext> channelMap = new HashMap<>();
    private static Map<Class, AbstractWrite> writeMap = new HashMap<>();
    private static Map<Class, AbstractRead> readMap = new HashMap<>();
//
    private static Set<Class<? extends AbstractRead>> readSet;
    private static Set<Class<? extends AbstractWrite>> writeSet;
//
    static{
        Reflections reflections = new Reflections("top.idcard.agent.biz");
        Set<Class<? extends AbstractRead>> readClassSet = reflections.getSubTypesOf(AbstractRead.class);
        Set<Class<? extends AbstractWrite>> writeClassSet = reflections.getSubTypesOf(AbstractWrite.class);
        readSet = readClassSet.stream().filter(item->!Modifier.isAbstract(item.getModifiers())).collect(Collectors.toSet());
        writeSet = writeClassSet.stream().filter(item->!Modifier.isAbstract(item.getModifiers())).collect(Collectors.toSet());
        writeSet.stream().forEach(item->{
            try {
                writeMap.put(getImplementWriteType(item), item.newInstance());
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        });
    }

    private RWContext(AbstractRead read){
        this.read = read;
    }

    private RWContext(AbstractWrite write){
        this.writeList.add(write);
    }

    public static RWContext getInstance(AbstractRead read){
        //获取实现的泛型类型
        Class entityClass = getImplementReadType(read.getClass());
        RWContext channelInstance = channelMap.getOrDefault(entityClass, null);
        if(channelInstance == null){
            channelInstance = new RWContext(read);
            //根据泛型类型获取Write
            AbstractWrite write = writeMap.get(entityClass);
            if(write != null && read.readToType().equals(write.dataFrom())) {
                channelInstance.addWrite(write);
            }
            channelMap.put(entityClass, channelInstance);
        }else{
            channelInstance.setRead(read);
        }
        return channelInstance;
    }

    public static RWContext getInstance(AbstractWrite write){
        Class entityClass = getImplementWriteType(write.getClass());
        RWContext channelInstance = channelMap.getOrDefault(entityClass, null);
        if (channelInstance == null) {
            channelInstance = new RWContext(write);
            AbstractRead read = readMap.get(entityClass);
            if(read != null && write.dataFrom().equals(read.readToType())){
                channelInstance.setRead(read);
            }
            channelMap.put(entityClass, channelInstance);
        }else{
            channelInstance.addWrite(write);
        }
        return null;
    }

    private static Class getImplementReadType(Type type) {
        if(type == null){
            return null;
        }
        Class typeClass = null;
        if(type instanceof Class){
            typeClass = getImplementReadType(((Class) type).getGenericSuperclass());
        } else if(type instanceof ParameterizedType){
            ParameterizedTypeImpl typeImpl = (ParameterizedTypeImpl) type;
            Type rawType = typeImpl.getRawType();
            String rawTypeClassName = rawType.getTypeName();
            if (rawTypeClassName.contains("top.idcard.agent.biz")) {
                Type[] types = ((ParameterizedType) type).getActualTypeArguments();
                typeClass = (Class) types[1];
            }
        }else if(type instanceof TypeVariable){
            //
        }else if(type instanceof GenericArrayType){
            //
        }else if(type instanceof WildcardType){
            //
        }
        return typeClass;
    }

    private static Class getImplementWriteType(Type type) {
        if(type == null){
            return null;
        }
        Class typeClass = null;
        if(type instanceof Class){
            typeClass = getImplementWriteType(((Class) type).getGenericSuperclass());
        } else if(type instanceof ParameterizedType){
            ParameterizedTypeImpl typeImpl = (ParameterizedTypeImpl) type;
            Type rawType = typeImpl.getRawType();
            String rawTypeClassName = rawType.getTypeName();
            if (rawTypeClassName.contains("top.idcard.agent.biz")) {
                Type[] types = ((ParameterizedType) type).getActualTypeArguments();
                typeClass = (Class) types[0];
            }
        }else if(type instanceof TypeVariable){
            //
        }else if(type instanceof GenericArrayType){
            //
        }else if(type instanceof WildcardType){
            //
        }
        return typeClass;
    }

    public D readData(){
        this.data = read.nextObject();
        return this.data;
    }

    public void writeData(){
        writeList.forEach(item->{
            item.writeObject(data);
        });
    }

    public void writeData(D data){
        writeList.forEach(item->{
            item.writeObject(data);
        });
    }

    public Read getRead() {
        return read;
    }

    public List<AbstractWrite<D>> getWriteList() {
        return writeList;
    }

    public D getData() {
        return data;
    }
    public void addWrite(AbstractWrite<D> write){
        this.writeList.add(write);
    }

    private void setRead(Read read){
        this.read = (AbstractRead<I, D>) read;
    }
}
